import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:base_lib/app.dart';
import 'package:base_lib/common/network/result_body.dart';
import 'package:base_lib/utils/util.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:http_parser/http_parser.dart';
import 'package:flutter/foundation.dart';
import '../../config.dart';
import '../../index.dart';
import 'loading_dialog.dart';
import 'log_intercept.dart';

typedef DioInitCallback(Dio dio);

///http请求
class HttpManager {
  static const CONTENT_TYPE_JSON = "application/json";
  static const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

  final DioInitCallback dioInitCallback;

  final List<Interceptor> defaultInterceptors;

  // ignore: non_constant_identifier_names
  int REQ_TIME_OUT;

  // ignore: non_constant_identifier_names
  int RESP_TIME_OUT;

  static Map<String, CancelToken> _cancelTokens =
      new Map<String, CancelToken>();

  Dio mJsonDio;

  Dio mDio;

  String mBaseUrl;

  // ignore: non_constant_identifier_names
  String CODE_NAME;

  // ignore: non_constant_identifier_names
  String MSG_NAME;

  // ignore: non_constant_identifier_names
  int CODE_SUCCESS_VALUE;

  // ignore: non_constant_identifier_names
  String LOG_TAG;

  HttpManager(this.mBaseUrl,
      // ignore: non_constant_identifier_names
      {this.CODE_NAME = "code",
      // ignore: non_constant_identifier_names
      this.CODE_SUCCESS_VALUE = 0,
      // ignore: non_constant_identifier_names
      this.MSG_NAME = "message",
      // ignore: non_constant_identifier_names
      this.REQ_TIME_OUT = 50000,
      // ignore: non_constant_identifier_names
      this.RESP_TIME_OUT = 50000,
      // ignore: non_constant_identifier_names
      this.LOG_TAG = "net",
      this.defaultInterceptors = const [],
      this.dioInitCallback}) {
    mDio = initDio(mBaseUrl);
    dioInitCallback?.call(mDio);

    mJsonDio = initJsonDio(mBaseUrl);
    dioInitCallback?.call(mJsonDio);
  }

  ///get请求
  Future<ResultBody<T>> get<T>(
    String url,
    T buildResBean(resMap), {
    Map<String, dynamic> queryParams,
    bool dealIsSuccess(resMap, errCode, errMsg),
    List<Interceptor> interceptors,
    String baseUrl,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: true,
        queryParams: queryParams,
        dealIsSuccess: dealIsSuccess,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  ///post请求
  Future<ResultBody<T>> post<T>(
    String url,
    T buildResBean(resMap), {
    Map<String, dynamic> queryParams,
    bool dealIsSuccess(resMap, errCode, errMsg),
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: false,
        queryParams: FormData.fromMap(queryParams),
        dealIsSuccess: dealIsSuccess,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  ///上传
  Future<ResultBody<T>> upload<T>(
    String url,
    T buildResBean(resMap),
    Map<String, File> files, {
    Map<String, String> params,
    bool dealIsSuccess(resMap, errCode, errMsg),
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) async {
    Map<String, dynamic> realParam = Map();

    if (params != null) realParam.addAll(params);

    var filePartMap = files.map((key, value) {
      var path = value.path;
      var name = path.substring(path.lastIndexOf("/") + 1, path.length);
      var suffix = name.substring(name.lastIndexOf(".") + 1, name.length);
      return MapEntry<String, MultipartFile>(
          key,
          MultipartFile.fromFileSync(path,
              filename: name, contentType: MediaType.parse("image/$suffix")));
    });

    realParam.addAll(filePartMap);

    FormData formData = new FormData.fromMap(realParam);

    return _requestHttp<T>(url, buildResBean,
        isGet: false,
        isJson: false,
        isUpload: true,
        formData: formData,
        dealIsSuccess: dealIsSuccess,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  ///json请求
  Future<ResultBody<T>> postJson<T>(
    String url,
    T buildResBean(resMap), {
    String jsonData,
    bool dealIsSuccess(resMap, errCode, errMsg),
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: false,
        isJson: true,
        jsonData: jsonData,
        dealIsSuccess: dealIsSuccess,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  static String formatErrorMessage(DioErrorType type) {
    switch (type) {
      case DioErrorType.DEFAULT:
        return "无法连接到服务器，请检查您的网络设置！";
      case DioErrorType.CONNECT_TIMEOUT:
        return "连接超时，请稍后再试！";
      case DioErrorType.RECEIVE_TIMEOUT:
        return "接收超时，请稍后再试！";
      case DioErrorType.RESPONSE:
        return "服务器睡着了，请稍后再试！";
      case DioErrorType.CANCEL:
        return "取消网络请求";
      default:
        return "未知网络错误";
    }
  }

  /// 参数说明  url 请求路径
  /// queryParams  是 请求参数
  /// isCancelable 是设置改请求是否 能被取消
  Future<ResultBody<T>> _requestHttp<T>(
    String url,
    T buildResBean(resMap), {
    bool isGet,
    bool isJson = false,
    String jsonData,
    bool isUpload = false,
    FormData formData,
    queryParams,
    bool dealIsSuccess(resMap, errCode, errMsg),
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) async {
    ResultBody resultBody;
    Future future;
    CancelToken cancelToken;
    _setInterceptOrCancelable(url, isCancelable, cancelToken);

    if (interceptors != null && interceptors.isNotEmpty) {
      mDio = initDio(mBaseUrl, interceptors: interceptors);
      dioInitCallback?.call(mDio);
    }
    if (isJson) {
      if (interceptors != null && interceptors.isNotEmpty) {
        mJsonDio = initJsonDio(mBaseUrl, interceptors: interceptors);
        dioInitCallback?.call(mJsonDio);
      }
      future = mJsonDio.post(url, data: jsonData, cancelToken: cancelToken);
    } else if (isGet) {
      future =
          mDio.get(url, queryParameters: queryParams, cancelToken: cancelToken);
    } else if (isUpload) {
      future = mDio.post(url, data: formData, cancelToken: cancelToken);
    } else {
      future = mDio.post(url, data: queryParams, cancelToken: cancelToken);
    }

    await future.then((data) {
      Utils.logLong("$LOG_TAG succ:${mBaseUrl + url}");
      Utils.logLong("$LOG_TAG succ:${data.toString()}");

      //这里要做错误处理
      //需要先过滤 error ， 根据和后台的 约定 ， 搞清楚什么是失败
      // 什么是成功  ， 这里只是根据了干货集中营的 返回 模拟了一下
      String res = data.toString();
      dynamic resMap = json.decode(res);
      int errCode = int.parse(resMap[CODE_NAME].toString());
      String errMsg = resMap[MSG_NAME] ?? "";

      dynamic realDealIsSuccess = dealIsSuccess ?? dealIsSuccessDefault;

      bool isSuccess = realDealIsSuccess(resMap, errCode, errMsg);

      ResultError err = ResultError(errMsg ?? "服务器响应错误", code: errCode);

      if (!isSuccess) {
        resultBody = ResultBody<T>(isSuccess, data: null, error: err);
      } else {
        resultBody =
            ResultBody<T>(isSuccess, data: buildResBean(resMap), error: err);
      }
    }).catchError((err) {
      Utils.logLong("$LOG_TAG error:${err.toString()}");
      resultBody = ResultBody<T>(false,
          error: ResultError(
              (err is DioError) ? formatErrorMessage(err.type) : err.toString(),
              code: -1));
    });

    Utils.logLong("$LOG_TAG result ${resultBody.toString()}");
    return resultBody;
  }

  bool dealIsSuccessDefault(resMap, errCode, errMsg) {
    return int.parse(errCode.toString()) == CODE_SUCCESS_VALUE;
  }

  ///测试是否真的 可以清除 的方法
  void listCancelTokens() {
    _cancelTokens.forEach((key, value) {
      Utils.logLong("$LOG_TAG  $key-----cancel---");
    });
  }

  ///取消请求
  void cancelHttp(String tag) {
    if (_cancelTokens.containsKey(tag)) {
      if (!_cancelTokens[tag].isCancelled) {
        _cancelTokens[tag].cancel();
      }
      _cancelTokens.remove(tag);
    }
  }

  ///配置是否能被取消
  void _setInterceptOrCancelable(
      String tag, bool isCancelable, CancelToken cancelToken) {
    //为了 能够取消 请求
    if (isCancelable) {
      cancelToken = _cancelTokens[tag] ?? CancelToken();
      _cancelTokens[tag] = cancelToken;
    }
  }

  ///配置dio
  Dio initJsonDio(baseUrl, {List<Interceptor> interceptors}) {
    return initDio(baseUrl,
        contentType: CONTENT_TYPE_JSON, interceptors: interceptors);
  }

  ///配置dio
  Dio initDio(baseUrl,
      {String contentType = CONTENT_TYPE_FORM,
      List<Interceptor> interceptors}) {
    Dio _dio = Dio();
    // 配置dio实例
    _dio.options.baseUrl = baseUrl;
    _dio.options.connectTimeout = REQ_TIME_OUT;
    _dio.options.receiveTimeout = RESP_TIME_OUT;
    _dio.options.contentType = contentType;

    //代理设置
    if (kDebugMode) {
      //此处可以增加配置项，留一个设置代理的用户给测试人员配置，然后动态读取
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        //访问Https忽略证书,完善SSL-HandShake问题
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return true;
        };

        if (App.httpProxy.isNotEmpty) {
          //config the http client
          client.findProxy = (uri) {
            return "PROXY ${App.httpProxy}";
          };
        }

        //you can also create a new HttpClient to dio
        //return new HttpClient();
      };
    } else
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        //访问Https忽略证书,完善SSL-HandShake问题
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return true;
        };
      };

    //代理设置
    //此处可以增加配置项，留一个设置代理的用户给测试人员配置，然后动态读取
//    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
//        (client) {
//      client.findProxy = (uri) {
//        return HttpClient.findProxyFromEnvironment(uri, environment: {
//          "http_proxy": 'http://192.168.0.248:8888',
//        });
//      };
//    };

    //证书配置
//    String PEM="XXXXX"; // certificate content
//    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate  = (client) {
//      client.badCertificateCallback=(X509Certificate cert, String host, int port){
//        if(cert.pem==PEM){ // Verify the certificate
//          return true;
//        }
//        return false;
//      };
//    };

    /// 添加拦截器
    interceptors ??= List<Interceptor>();

    _dio.interceptors.addAll(interceptors);

    _dio.interceptors.addAll(this.defaultInterceptors);

    _dio.interceptors.add(DoctorBaseInterceptor());

    ///cookie 管理
    var cookieJar = CookieJar();

    _dio.interceptors..add(CookieManager(cookieJar))..add(LogIntercept());

    return _dio;
  }
}

class DoctorBaseInterceptor extends Interceptor {
  @override
  Future onRequest(RequestOptions options) {
    // LoadingDialog.before(options.path);
    return super.onRequest(options);
  }

  @override
  Future onResponse(Response response) {
    // LoadingDialog.complete(response.request.path);
    String res = response.toString();
    dynamic resMap = json.decode(res);
    int errCode = int.parse(resMap[Config.codeName].toString());
    String errMsg = resMap[Config.msgName].toString();

    ///处理token过期
    if (errCode == 401 && errMsg == 'Token invalid') {
      ToastUtil.error('token失效，请重新登录');
      Routers.navigateTo(App.navigatorKey.currentContext, Routers.login_page,
          clearStack: true);
    }

    return super.onResponse(response);
  }
}
